#!/bin/sh

touch ${FLAG_FILE}	# create this flag file once patch process start

# step 1: include product specific logic
. ${PACKAGE_PRODUCT}
. ${UPDATE_TOOL_FOLDER}/common

declare -A HIDDEN_PARTS_ARR
declare -A SNAPSHOT_PARTS_ARR
declare -A REMOVE_PARTS_ARR
# number of hidden partition
NUM_HIDDEN_PARTS=0
# number of snapshot partition
NUM_SNAPSHOT_PARTS=0
# number of will be removed this time
NUM_REMOVE_PARTS=0
# new root system directory
SYS_ROOT_DIR=""
# new app root directory
APP_ROOT_DIR=""
# volume group name
VGNAME=""
# new version boot place
NEW_OS_PLACE=""
# number of current snapshot partitions
NUM_SNAPSHOT_RUNNING=0
# boot partition backup file
BOOT_BACKUP="/root/`uname -r`-backup.tgz"
# error code
ERROR_NUMBER=0
ROLLBACK_FILE=${UPDATE_TOOL_FOLDER}/.upgrade_flag
NewDataPartMountPoint=""
# make file system cmd
MKFS_CMD="mkfs.ext4"

VMDETECTOR=${UPDATE_TOOL_FOLDER}/vmdetector

F_os_pre_check() 
{
    # step 1: check os version dependency
    local cur_os_ver=`uname -r | sed -e 's/^.*OpenVA.//' | cut -d'.' -f1-2`
    local dep_os_ver=`F_ini_get $PACKAGE_CONFIG Upgrade Dependency | awk -F',' '{print $1}' | cut -d'/' -f 1`
    local dep_os_ver2=`F_ini_get $PACKAGE_CONFIG Upgrade Dependency | awk -F',' '{print $2}' | cut -d'/' -f 1`

    [ -n "$cur_os_ver" -a -n "$dep_os_ver" ] || {
        F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "Can't get dependency information."
        ERROR_NUMBER=$ERR_INI_FILE_CONTENT
        return 1
    }

#    [ $cur_os_ver == $dep_os_ver ] || {
    ( [ $cur_os_ver == $dep_os_ver ] || ( [ -n "${dep_os_ver2}" ] && [ $cur_os_ver == ${dep_os_ver2} ] )) || {
        F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "OS version dependency check failed."
        ERROR_NUMBER=$ERR_OPENVA_PRE_CHECK
        return 1; 
    }

    return 0
}

F_pre_check() 
{
    F_prompt_log "Start Pre-Check step."

    if ! F_function_exist F_product_pre_check; then
        F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "Function \"F_product_pre_check\" doesn't exist."
        F_prompt_log "Unable to find function \"F_product_pre_check\". File \"productUpdate.sh\" should define function \"F_product_pre_check\". ${ERRMSG_REGEN_PKG}."
        ERROR_NUMBER=$ERR_FUNC_MISSING
        return 1
    fi

    if ! F_function_exist F_product_pre_apply; then
        F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "Function \"F_product_pre_apply\" doesn't exist."
        F_prompt_log "Unable to find function \"F_product_pre_apply\". File \"productUpdate.sh\" should define function \"F_product_pre_apply\". ${ERRMSG_REGEN_PKG}."
        ERROR_NUMBER=$ERR_FUNC_MISSING
        return 1
    fi

    if ! F_function_exist F_product_upgrade; then
        F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "Function \"F_product_upgrade\" doesn't exist."
        F_prompt_log "Unable to find function \"F_product_upgrade\". File \"productUpdate.sh\" should define function \"F_product_upgrade\". ${ERRMSG_REGEN_PKG}."
        ERROR_NUMBER=$ERR_FUNC_MISSING
        return 1
    fi

    if ! F_function_exist F_product_pre_import; then
        F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "Function \"F_product_pre_import\" doesn't exist."
        F_prompt_log "Unable to find function \"F_product_pre_import\". File \"productUpdate.sh\" should define function \"F_product_pre_import\". ${ERRMSG_REGEN_PKG}."
        ERROR_NUMBER=$ERR_FUNC_MISSING
        return 1
    fi

    if ! F_function_exist F_product_post_apply; then
        F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "Function \"F_product_post_apply\" doesn't exist."
        F_prompt_log "Unable to find function \"F_product_post_apply\". File \"productUpdate.sh\" should define function \"F_product_post_apply\". ${ERRMSG_REGEN_PKG}."
        ERROR_NUMBER=$ERR_FUNC_MISSING
        return 1
    fi

    # step 1: check if requirement meets for openva 
    F_os_pre_check || {
        F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "Pre-Check of OpenVA failed." 
        F_prompt_log "Unable to pass the required OS dependency. Upgrade with dependency can be applied only to the required OS version. This upgrade package cannot be applied to this OS version."
        return 1
    } && {
        F_prompt_log "Pre-Check of OpenVA succeeded."
    }

    # step 2: call product checking
    F_product_pre_check || {
        F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "Pre-Check of Product failed."
        F_prompt_log "Product defined pre-check function \"F_product_pre_check\" execute failed. ${ERRMSG_ASK_HELP}."
        ERROR_NUMBER=${ERR_PRODUCT_PRE_CHECK}
        return 1
    } && {
        F_prompt_log "Pre-Check of product succeeded."
    }

    return 0
}

F_os_pre_upgrade() 
{
    # get the hidden partition and snapshot partitions
    local HiddenParts=`F_ini_get $PACKAGE_CONFIG Partition HiddenParts`
    local SnapshotParts=`F_ini_get $PACKAGE_CONFIG Partition SnapshotParts`
    #local MountDevInsteadParts=`F_ini_get $PACKAGE_CONFIG Partition MountDevInstead`
    local RemovePartPair=`F_ini_get $PACKAGE_CONFIG Partition RemovePartPair`

    [ -n "$HiddenParts" ] || {
        F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "Can't get HiddenParts field from file $PACKAGE_CONFIG."
        F_prompt_log "Unable to locate the key value for \"HiddenParts\" in the \"Partition\" section of \"config.ini\". This key value must be set properly. ${ERRMSG_REGEN_PKG}."
        ERROR_NUMBER=$ERR_INI_FILE_CONTENT
        return 1
    }

    [ -n "$SnapshotParts" ] || {
        F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "Can't get SnapshotParts field from file $PACKAGE_CONFIG."
        F_prompt_log "Unable to locate the key value for \"SnapshotParts\" in the \"Partition\" section of \"config.ini\". This key value must be set properly. ${ERRMSG_REGEN_PKG}."
        ERROR_NUMBER=$ERR_INI_FILE_CONTENT
        return 1
    }

    left_chars=`echo $HiddenParts | tr -d "[:alnum:]" | tr -d / | tr -d '_' | tr -d ','`
    if [ -n "$left_chars" ]; then
        F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "The format of hidden partition pairs isn't correct."
        F_prompt_log "The partition pair format is incorrect and must follow the required format. ${ERRMSG_ASK_HELP}."
        ERROR_NUMBER=$ERR_INI_FILE_CONTENT
        return 1
    fi

    left_chars=`echo $SnapshotParts | tr -d "[:alnum:]" | tr -d / | tr -d '_' | tr -d ','`
    if [ -n "$left_chars" ]; then
        F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "The format of data partition pairs isn't correct."
        F_prompt_log "The data partition pair format is incorrect and must follow the required format. ${ERRMSG_ASK_HELP}."
        ERROR_NUMBER=$ERR_INI_FILE_CONTENT
        return 1
    fi

    local HiddenPartsArr=(`echo $HiddenParts | sed -e 's/,/ /g'`)
    local SnapshotPartsArr=(`echo $SnapshotParts | sed -e 's/,/ /g'`)

    # for binary partitions
    NUM_HIDDEN_PARTS=${#HiddenPartsArr[@]} 
    NUM_SNAPSHOT_PARTS=${#SnapshotPartsArr[@]}

    [ "$NUM_HIDDEN_PARTS" -ge 1 -a "$NUM_HIDDEN_PARTS" -le 2 ] || {
        F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "Number of hidden partition can't exceed two."
        F_prompt_log "The number of hidden partitions is incorrect and must not exceed two (2). ${ERRMSG_ASK_HELP}."
        ERROR_NUMBER=$ERR_NUM_HIDDEN_PARTS
        return 1
    }

    # Check MountDevInstead
#    if [ -n "${MountDevInsteadParts}" ]; then
#        left_chars=`echo ${MountDevInsteadParts} | tr -d "[:alnum:]" | tr -d / | tr -d '_' | tr -d ','`
#        if [ -n "$left_chars" ]; then
#            F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "The format of mount device instead pairs isn't correct."
#            F_prompt_log "The mount device instead pair format is incorrect and must follow the required format. ${ERRMSG_ASK_HELP}."
#            ERROR_NUMBER=$ERR_INI_FILE_CONTENT
#            return 1
#        fi
#        eval local old_mountdev=${MountDevInsteadParts/,/; local new_mountdev=}
#        if [ "$old_mountdev" = "$new_mountdev" ] || [ ! -n "$old_mountdev" ] || [ ! -n "$new_mountdev" ]; then
#            F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "The format of mount device instead pairs isn't correct."
#            F_prompt_log "The mount device instead pair format is incorrect and must follow the required format. ${ERRMSG_ASK_HELP}."
#            ERROR_NUMBER=$ERR_INI_FILE_CONTENT
#            return 1
#        fi
#        mount | grep -qwE "$new_mountdev"
#        if [ $? -ne 0 ]; then
#            local old_mountdev=""
#            local new_mountdev=""
#        fi
#    else
#        local old_mountdev=""
#        local new_mountdev=""
#    fi

    # these lvs should just in one vg
    VGNAME=`vgdisplay -c | head -1 | sed -e 's/^[ ]*\([^:]*\).*/\1/'`
    [ -n "$VGNAME" ] || {
        F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "Can't get volume group name."
        ERROR_NUMBER=$ERR_VGNAME
        return 1
    }

    # Check vg duplicate
    local num=`vgs 2>/dev/null |grep -w " ${VGNAME} " | wc -l`
    if [ $num -ne 1 ]; then
	F_debug_log "There are more than 1 volume groups named ${VGNAME}. Need check which one is the right one and remove the others."

	# get redundant vg(s)
	local rvg_uuid=`F_get_redundant_vg_by_attr ${VGNAME}`
	local RET=$?
	rvg_uuid=`echo ${rvg_uuid}` #trim
	if [ $RET -ne 0 ] || [ "${rvg_uuid}" = "" ]; then
	    F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "Can't get redundant volumn group(s). Please use vgs or vgdisplay to check manually."
	    F_prompt_log "There are more than 1 volume groups named ${VGNAME} but can't get the redundant one(s). ${ERRMSG_ASK_HELP}."
	    ERROR_NUMBER=$ERR_DUPLICATE_VGNAME
	    return 1
	fi

	# remove redundant vg(s)
	F_debug_log "Those volume group(s) by uuid need be removed: ${rvg_uuid}."	
	for vg_uuid in ${rvg_uuid}
	do
	    # for partial vg, only can be removed by removing its pv
	    F_remove_pv_by_vg ${vg_uuid} || {
		F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "Can't remove volumn group ${vg_uuid}."
            	F_prompt_log "There are more than 1 volume groups named ${VGNAME} but can't remove the redundant one(s). ${ERRMSG_ASK_HELP}."
            	ERROR_NUMBER=$ERR_DUPLICATE_VGNAME
            	return 1
	    }
	done

	# check if there is any left redundant vg
	num=`vgs 2>/dev/null |grep -w " ${VGNAME} " | wc -l`
	if [ $num -ne 1 ]; then
	    F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "There are still more than 1 volume groups named ${VGNAME}. Need check it manually."
            F_prompt_log "There are more than 1 volume groups named ${VGNAME} but can't remove the redundant one(s) automatically. Need check it manually. ${ERRMSG_ASK_HELP}."
            ERROR_NUMBER=$ERR_DUPLICATE_VGNAME
            return 1
	fi
    fi

    # If grub 0.9x, need check if disk uses GPT partition
    grub --version 2>/dev/null |grep "0.9" >/dev/null 2>&1
    if [ $? -eq 0 ]; then
	F_debug_log "Grub version is 0.9x, need check if disk(s) use GPT partition."
	F_check_gpt_by_vg ${VGNAME} || {
	    F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "Don't support migration from grub 0.9x to grub2 while disks(s) using GPT partition."
	    F_prompt_log "Don't support migration from grub 0.9x to grub2 while disks(s) using GPT partition. ${ERRMSG_ASK_HELP}."
	    ERROR_NUMBER=$ERR_GPT_DISK
	    return 1
	}
    else
	F_debug_log "Grub version is not 0.9x, skip to check GPT partition."
    fi

    if [ ! -n "$RemovePartPair" ]; then
        F_debug_log "There isn't any partition pair need to be removed."
        REMOVE_PARTS_ARR["old_remove_partition"]=""
        REMOVE_PARTS_ARR["new_remove_partition"]=""
    else
        eval local remove_firstpart=${RemovePartPair/\//; local remove_secondpart=}
        if [ "${remove_firstpart}" = "${remove_secondpart}" ]; then
	    F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "The first and the second part of the remove partition pair is the same."
            F_prompt_log "The first and the second part of the remove partition pairs are identical, and must not match. ${ERRMSG_ASK_HELP}."
            ERROR_NUMBER=$ERR_LV
	    return 1
        fi
        cat /etc/mtab |grep -q "$VGNAME-${remove_firstpart}\b"
        local ret1=$?
        cat /etc/mtab |grep -q "$VGNAME-${remove_secondpart}\b"
        local ret2=$?
        if [ ${ret1} -ne 0 ] && [ ${ret2} -ne 0 ]; then
            # app and app2 both not mounted, but maybe the LVM still exist, need remove them both at this time
            NUM_REMOVE_PARTS=2
            REMOVE_PARTS_ARR["old_remove_partition"]="/dev/mapper/${VGNAME}-${remove_firstpart}"
            REMOVE_PARTS_ARR["new_remove_partition"]="/dev/mapper/${VGNAME}-${remove_secondpart}"
            F_debug_log "The lvm ${remove_firstpart} and ${remove_secondpart} will be removed."
        elif [ ${ret1} -eq 0 ]; then
            NUM_REMOVE_PARTS=1
            REMOVE_PARTS_ARR["old_remove_partition"]="/dev/mapper/${VGNAME}-${remove_firstpart}"
            REMOVE_PARTS_ARR["new_remove_partition"]="/dev/mapper/${VGNAME}-${remove_secondpart}"
            F_debug_log "The lvm ${remove_secondpart} will be removed."
        else
            NUM_REMOVE_PARTS=1
            REMOVE_PARTS_ARR["old_remove_partition"]="/dev/mapper/${VGNAME}-${remove_secondpart}"
            REMOVE_PARTS_ARR["new_remove_partition"]="/dev/mapper/${VGNAME}-${remove_firstpart}"
            F_debug_log "The lvm ${remove_firstpart} will be removed."
        fi
    fi

    local flag=0
    for (( i=0;  i<$NUM_HIDDEN_PARTS; i++ )) ;
    do
        # ugly code
        local hiddenpair=${HiddenPartsArr[i]} 
        eval local firstpart=${hiddenpair/\//; local secondpart=}


        if [ "$firstpart" = "$secondpart" ]; then
            F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "The first and the second part of the hidden partition pair is the same."
            F_prompt_log "The first and the second part of the hidden partition pairs are identical, and must not match. ${ERRMSG_ASK_HELP}."
            flag=1
            break;
        fi

        lvdisplay "/dev/${VGNAME}/${firstpart}" > /dev/null 2>&1 ||  {
            F_debug_log "The logical volume: $firstpart doesn't exist."
# don't return fail but just print log when hidden partition doesn't exist,
# since hidden partition may be lvremove already.
#            flag=1
#            break
        }

        lvdisplay "/dev/${VGNAME}/${secondpart}" > /dev/null 2>&1 || {
            F_debug_log "The logical volume: $secondpart doesn't exist."
# don't return fail but just print log when hidden partition doesn't exist,
# since hidden partition may be lvremove already.
#            flag=1
#            break
        }

        cat /etc/mtab |grep -q "$VGNAME-$firstpart\b"
        if [ $? -eq 0 ]; then
            HIDDEN_PARTS_ARR["old_`test $i -eq 0 && echo "root" || echo "app"`_partition"]="/dev/mapper/${VGNAME}-${firstpart}"
            HIDDEN_PARTS_ARR["new_`test $i -eq 0 && echo "root" || echo "app"`_partition"]="/dev/mapper/${VGNAME}-${secondpart}"
        else
            HIDDEN_PARTS_ARR["old_`test $i -eq 0 && echo "root" || echo "app"`_partition"]="/dev/mapper/${VGNAME}-${secondpart}"
            HIDDEN_PARTS_ARR["new_`test $i -eq 0 && echo "root" || echo "app"`_partition"]="/dev/mapper/${VGNAME}-${firstpart}"
        fi
    done

    [ $flag == 0 ] || {
        ERROR_NUMBER=$ERR_LV
        return 1
    }

    F_prompt_log "Old root partition is ${HIDDEN_PARTS_ARR["old_root_partition"]}."
    F_prompt_log "New root partition is ${HIDDEN_PARTS_ARR["new_root_partition"]}."

    if [ $NUM_HIDDEN_PARTS -gt 1 ]; then
        F_prompt_log "Old application partition is ${HIDDEN_PARTS_ARR["old_app_partition"]}."
        F_prompt_log "New application partition is ${HIDDEN_PARTS_ARR["new_app_partition"]}."
    fi

    # snapshot partition
    F_prompt_log "Skip check snapshot logica volume."
#    for (( i=0; i<$NUM_SNAPSHOT_PARTS; i++ )) ;
#    do
#        local sspair=${SnapshotPartsArr[i]}
#        eval local firstpart=${sspair/\//; local secondpart=}
#
#        if [ "$firstpart" = "$secondpart" ]; then
#            F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "The original and snapshot logical volume is the same."
#            F_prompt_log "The original and snapshot logical volume is the same. It should different. ${ERRMSG_ASK_HELP}."
#            flag=1
#            break;
#        fi
#
#        lvdisplay "/dev/${VGNAME}/$firstpart" > /dev/null 2>&1 || {
#            F_debug_log "The logical volume: $firstpart doesn't exist."
#            flag=1
#            break
#        }
#
#        lvdisplay "/dev/${VGNAME}/$secondpart" > /dev/null 2>&1 || {
#            F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "The logical volume: $secondpart doesn't exist."
## don't return fail but just print log when snapshot partition doesn't exist,
## since hidden partition may be lvremove already.
##            flag=1
##            break
#        }
#
#        #if [ -n "${old_mountdev}" ] && [ -n "${new_mountdev}" ] && [ "${firstpart}" = "${old_mountdev}" ]; then
#        #    SNAPSHOT_PARTS_ARR["org_data_partition$i"]="${new_mountdev}"
#        #else
#            SNAPSHOT_PARTS_ARR["org_data_partition$i"]="/dev/mapper/${VGNAME}-${firstpart}"
#        #fi
#        SNAPSHOT_PARTS_ARR["snapshot_data_partition$i"]="/dev/mapper/${VGNAME}-${secondpart}"
#
#        F_prompt_log "Snapshot logical volume for ${firstpart} is ${secondpart}."
#    done

    [ $flag == 0 ] || {
        ERROR_NUMBER=$ERR_LV
        return 1
    }

    F_add_recovery_func "F_die_umount"

    SYS_ROOT_DIR=`cat /etc/fstab | grep -Ew "${HIDDEN_PARTS_ARR["new_root_partition"]}" | sed -e 's/^[^ ]*[ ]*\([^ ]*\).*/\1/'`
    [ -n "$SYS_ROOT_DIR"  ] || {
        F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "Empty system root directory."
        F_prompt_log "Unable to get new system root directory from the file: \"/etc/fstab\". ${ERRMSG_ASK_HELP}."
        ERROR_NUMBER=$ERR_EMPTY_ROOT_DIR
        return 1
    }

    # umount data partition from new root partition if have
    local MountDatatoNewRoot=`F_ini_get $PACKAGE_CONFIG Partition MountDatatoNewRoot`
    local DataPartMountPoint=`F_ini_get $PACKAGE_CONFIG Partition DataPartMountPoint`
    if [ "${MountDatatoNewRoot}" = "yes" ]; then
        [ -n "${DataPartMountPoint}" ] || {
            F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "Can't get DataPartMountPoint field from file $PACKAGE_CONFIG."
            F_prompt_log "Unable to locate the key value for \"DataPartMountPoint\" in the \"Partition\" section of \"config.ini\". This key value must be set properly if \"MountDatatoNewRoot\" is set. ${ERRMSG_REGEN_PKG}."
            ERROR_NUMBER=$ERR_INI_FILE_CONTENT
            return 1
        }

        NewDataPartMountPoint=${SYS_ROOT_DIR}${DataPartMountPoint}
        mount | grep -qwE "${NewDataPartMountPoint}" 
        if [ $? -eq 0 ]; then
            F_prompt_log "Umount ${NewDataPartMountPoint}."
            umount ${NewDataPartMountPoint} > /dev/null 2>&1 || {
                F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "Can't umount directory: ${NewDataPartMountPoint}."
                F_prompt_log "Unable to un-mount directory: \"${NewDataPartMountPoint}\". ${ERRMSG_ASK_HELP}."
                ERROR_NUMBER=$ERR_UMOUNT
                return 1
            }
        fi
    fi

    # umount app if have
    if [ $NUM_HIDDEN_PARTS -gt 1 ] || [ "${REMOVE_PARTS_ARR["old_remove_partition"]}" != "" ]; then
        if [ $NUM_HIDDEN_PARTS -gt 1 ]; then
            APP_ROOT_DIR=`cat /etc/fstab | grep -Ew "${HIDDEN_PARTS_ARR["old_app_partition"]}" | sed -e 's/^[^ ]*[ ]*\([^ ]*\).*/\1/'`
        else
            APP_ROOT_DIR=`cat /etc/fstab | grep -Ew "${REMOVE_PARTS_ARR["old_remove_partition"]}" | sed -e 's/^[^ ]*[ ]*\([^ ]*\).*/\1/'`
        fi
        [ -n "$APP_ROOT_DIR" ] || {
            F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "Empty application root directory."
            F_prompt_log "Unable to get new application root directory from the file: \"/etc/fstab\". ${ERRMSG_ASK_HELP}."
            ERROR_NUMBER=$ERR_EMPTY_APP_DIR
            return 1
        }

        mount | grep -qwE "${SYS_ROOT_DIR}${APP_ROOT_DIR}"
        if [ $? -eq 0 ]; then
            F_prompt_log "Umount ${SYS_ROOT_DIR}${APP_ROOT_DIR}."
            umount ${SYS_ROOT_DIR}${APP_ROOT_DIR} > /dev/null 2>&1 || {
                F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "Can't umount directory: ${SYS_ROOT_DIR}${APP_ROOT_DIR}."
                F_prompt_log "Unable to un-mount directory: \"${SYS_ROOT_DIR}${APP_ROOT_DIR}\". ${ERRMSG_ASK_HELP}."
                ERROR_NUMBER=$ERR_UMOUNT
                return 1
            }
        fi
    fi

    # get new root lvm size
    local nsize=`F_ini_get $PACKAGE_CONFIG Partition NewRootPartSize`
    if [ ! -n "${nsize}" ]; then
        sizein_le=`F_get_lvsize ${HIDDEN_PARTS_ARR["new_root_partition"]}`
    else
        sizein_le=`expr ${nsize} \* 32`
    fi

    # drop new root lvm if exists
    lvdisplay -c ${HIDDEN_PARTS_ARR["new_root_partition"]} > /dev/null 2>&1 
    if [ $? -eq 0 ]; then
        F_lv_remove ${HIDDEN_PARTS_ARR["new_root_partition"]} || {
            F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "Unable to remove the new root lvm."
            ERROR_NUMBER=$ERR_LV
            return 1
        }
    fi

    # drop new app lvm if config.ini speicifies.
    if [ $NUM_HIDDEN_PARTS -le 1 ] && [ "${REMOVE_PARTS_ARR["new_remove_partition"]}" != "" ]; then
        if [ $NUM_REMOVE_PARTS -gt 1 ]; then
            lvdisplay -c ${REMOVE_PARTS_ARR["old_remove_partition"]} > /dev/null 2>&1
            if [ $? -eq 0 ]; then
                F_lv_remove ${REMOVE_PARTS_ARR["old_remove_partition"]} || {
                    F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "Unable to remove the old app lvm."
                    ERROR_NUMBER=$ERR_LV
                    return 1
                }
            fi
        fi
        lvdisplay -c ${REMOVE_PARTS_ARR["new_remove_partition"]} > /dev/null 2>&1
        if [ $? -eq 0 ]; then
            F_lv_remove ${REMOVE_PARTS_ARR["new_remove_partition"]} || {
                F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "Unable to remove the new app lvm."
                ERROR_NUMBER=$ERR_LV
                return 1
            }
        fi
    fi

    # create new root lvm
    local noprompt=""
    lvcreate --version | grep "LVM version:" | grep "RHEL6" >/dev/null 2>&1
    if [ $? -ne 0 ]; then
        noprompt="--yes"
    fi
    lvcreate --extents ${sizein_le} --name=${HIDDEN_PARTS_ARR["new_root_partition"]##*$VGNAME-} ${VGNAME} ${noprompt} > /dev/null 2>&1 || {
        F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "Unable to create the new root lvm."
        ERROR_NUMBER=$ERR_LV
        return 1
    }

    # make file system
    local fs=`cat /etc/fstab |grep "${HIDDEN_PARTS_ARR["new_root_partition"]##*$VGNAME-}\b" | awk '{print $3}'`
    if [ "$fs" = "ext3" ]; then
        MKFS_CMD="mkfs.ext3"
    fi
    ${MKFS_CMD} ${HIDDEN_PARTS_ARR["new_root_partition"]} > /dev/null 2>&1 || {
        F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "Unable to ${MKFS_CMD} on the new root lvm."
        ERROR_NUMBER=$ERR_LV
        return 1
    }

    # mount new root partition
    mkdir -p $SYS_ROOT_DIR
    F_prompt_log "Mount device: ${HIDDEN_PARTS_ARR["new_root_partition"]} at the directory: ${SYS_ROOT_DIR}."
    mount ${HIDDEN_PARTS_ARR["new_root_partition"]} ${SYS_ROOT_DIR} || {
        F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "Upgrade didn't succeed, because of the mounting of the upgrade disk partition."
        ERROR_NUMBER=$ERR_MOUNT
        return 1
    }

    # clear new root partition
    F_prompt_log "Clean new root partition ${SYS_ROOT_DIR}."
    rm -rf ${SYS_ROOT_DIR}/*

    # clear new app partition if it's kept
    if [ $NUM_HIDDEN_PARTS -gt 1 ]; then
	lvdisplay -c ${HIDDEN_PARTS_ARR["new_app_partition"]} >/dev/null 2>&1
	if [ $? -eq 0 ]; then
	    # new app lvm exists
	    mkdir -p ${SYS_ROOT_DIR}${APP_ROOT_DIR}
            F_prompt_log "Mount device: ${HIDDEN_PARTS_ARR["new_app_partition"]} at the directory: ${SYS_ROOT_DIR}${APP_ROOT_DIR}."
            mount ${HIDDEN_PARTS_ARR["new_app_partition"]} ${SYS_ROOT_DIR}${APP_ROOT_DIR} || {
                umount ${SYS_ROOT_DIR}
                F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "Upgrade didn't succeed, because of the mounting of the upgrade disk partition."
                ERROR_NUMBER=$ERR_MOUNT
                return 1
            }
            F_prompt_log "Clean new application binary partition ${SYS_ROOT_DIR}${APP_ROOT_DIR}."
            rm -rf ${SYS_ROOT_DIR}${APP_ROOT_DIR}/*
	else
	    # new app lvm doesn't exists, need re-create it
            local appsizein_le=`F_get_lvsize ${HIDDEN_PARTS_ARR["old_app_partition"]}`
	    lvcreate --extents ${appsizein_le} --name=${HIDDEN_PARTS_ARR["new_app_partition"]##*$VGNAME-} ${VGNAME} ${noprompt} > /dev/null 2>&1 || {
                umount ${SYS_ROOT_DIR}
		F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "Unable to create the new app lvm."
		ERROR_NUMBER=$ERR_LV
		return 1
	    }

	    ${MKFS_CMD} ${HIDDEN_PARTS_ARR["new_app_partition"]} > /dev/null 2>&1 || {
                umount ${SYS_ROOT_DIR}
		F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "Unable to ${MKFS_CMD} on the new app lvm."
		ERROR_NUMBER=$ERR_LV
		return 1
	    }
	fi
    fi

    # mount data partition to new root partition
    if [ "${MountDatatoNewRoot}" = "yes" ]; then
        [ -d ${NewDataPartMountPoint} ] || mkdir -p ${NewDataPartMountPoint}
        local DataMountDevice=`mount | grep -wE "${DataPartMountPoint}" | cut -d' ' -f1`
        [ -n "${DataMountDevice}" ] || {
            umount ${SYS_ROOT_DIR}
            if [ $NUM_HIDDEN_PARTS -gt 1 ]; then
                umount ${SYS_ROOT_DIR}${APP_ROOT_DIR}
            fi
            F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "Upgrade didn't succeed, because can't get the data device."
            ERROR_NUMBER=$ERR_MOUNT
            return 1

        }
        F_prompt_log "Mount device: ${DataMountDevice} at the directory: ${NewDataPartMountPoint}."
        mount ${DataMountDevice} ${NewDataPartMountPoint} || {
            umount ${SYS_ROOT_DIR}
            if [ $NUM_HIDDEN_PARTS -gt 1 ]; then
                umount ${SYS_ROOT_DIR}${APP_ROOT_DIR}
            fi
            F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "Upgrade didn't succeed, because of the mounting of the upgrade disk partition."
            ERROR_NUMBER=$ERR_MOUNT
            return 1
        }
    fi

    F_prompt_log "Mount and clean new partition completed."
    
    return 0
}

F_pre_upgrade() 
{
    F_prompt_log "Start Pre-Upgrade step."
    # step 1: openva pre-upgrade 
    F_os_pre_upgrade || {
        F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "Pre-Upgrade of OS failed." 
        return 1
    } && {
        F_prompt_log "Pre-Upgrade of OS succeeded."
    }

    # step 2: product
    F_product_pre_apply || {
        F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "Pre-Upgrade of Product failed."
        F_prompt_log "Product defined pre-apply function \"F_product_pre_apply\" execute failed. ${ERRMSG_ASK_HELP}."
        ERROR_NUMBER=${ERR_PRODUCT_PRE_APPLY}
        return 1
    } && {
        F_prompt_log "Pre-Upgrade of Product succeeded."
    }
    return 0
}

F_make_snapshot() 
{
    F_prompt_log "Make snapshot for data partitions."
    local flag=0
    for (( i=0; i<$NUM_SNAPSHOT_PARTS; i++ )) ;
    do
        local org_part=${SNAPSHOT_PARTS_ARR["org_data_partition$i"]}
        local snapshot_part=${SNAPSHOT_PARTS_ARR["snapshot_data_partition$i"]}
        local sizein_le=`F_get_lvsize $snapshot_part`

        F_lv_remove $snapshot_part || {
            F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "Can't remove logical volume $snapshot_part."
            F_prompt_log "Unable to remove logical volume: \"$snapshot_part\". ${ERRMSG_ASK_HELP}."
            flag=1
            break
        }

        F_prompt_log "Create snapshot for logical volume: $org_part."
        lvcreate --snapshot --extents ${sizein_le} --name ${snapshot_part##*$VGNAME-} /dev/$VGNAME/${org_part##*$VGNAME-} > /dev/null 2>&1 || { 
        #lvcreate --snapshot --extents ${sizein_le} --name ${snapshot_part##*$VGNAME-} ${org_part} > /dev/null 2>&1 || { 
            F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "Can't create snapshot logical volume $snapshot_part."; 
            F_prompt_log "Unable to create snapshot of logical volume: \"$snapshot_part\". ${ERRMSG_ASK_HELP}."
            flag=1
            break
        }
    done

    NUM_SNAPSHOT_RUNNING=$i

    # register recovery function
    F_add_recovery_func "F_die_aftersnapshot"
    
    [ $flag == 0 ] || {
        ERROR_NUMBER=$ERR_LV
        return 1
    }

    return 0
}

F_gen_rollback_file()
{
    echo "declare -A HIDDEN_PARTS_ARR" >> $ROLLBACK_FILE
    echo "declare -A SNAPSHOT_PARTS_ARR" >> $ROLLBACK_FILE
    echo "NUM_HIDDEN_PARTS=$NUM_HIDDEN_PARTS" >> $ROLLBACK_FILE
    echo "NUM_SNAPSHOT_PARTS=$NUM_SNAPSHOT_PARTS" >> $ROLLBACK_FILE
    echo "SYS_ROOT_DIR=$SYS_ROOT_DIR" >> $ROLLBACK_FILE
    echo "APP_ROOT_DIR=$APP_ROOT_DIR" >> $ROLLBACK_FILE
    echo "VGNAME=$VGNAME" >> $ROLLBACK_FILE
    echo "NUM_SNAPSHOT_RUNNING=$NUM_SNAPSHOT_RUNNING" >> $ROLLBACK_FILE

    echo "HIDDEN_PARTS_ARR["new_root_partition"]=${HIDDEN_PARTS_ARR["new_root_partition"]}" >> $ROLLBACK_FILE
    echo "HIDDEN_PARTS_ARR["old_root_partition"]=${HIDDEN_PARTS_ARR["old_root_partition"]}" >> $ROLLBACK_FILE

    if [ $NUM_HIDDEN_PARTS -ge 2 ]; then
        echo "HIDDEN_PARTS_ARR["new_app_partition"]=${HIDDEN_PARTS_ARR["new_app_partition"]}" >> $ROLLBACK_FILE
        echo "HIDDEN_PARTS_ARR["old_app_partition"]=${HIDDEN_PARTS_ARR["old_app_partition"]}" >> $ROLLBACK_FILE
    fi

    for (( i=0; i<$NUM_SNAPSHOT_PARTS; i++ )) ;
    do
        echo "SNAPSHOT_PARTS_ARR["org_data_partition$i"]=${SNAPSHOT_PARTS_ARR["org_data_partition$i"]}" >> $ROLLBACK_FILE
        echo "SNAPSHOT_PARTS_ARR["snapshot_data_partition$i"]=${SNAPSHOT_PARTS_ARR["snapshot_data_partition$i"]}" >> $ROLLBACK_FILE
    done

    echo "BOOT_BACKUP=$BOOT_BACKUP" >> $ROLLBACK_FILE

    return 0
}

F_upgrade() 
{
    F_prompt_log "Install new packages, this may take a while."

    # just echo command, no error check for this
    F_gen_rollback_file

#    rpm_list_all=$(cd $UNPACK_DIR/packages/ ; ls *.rpm)
#    for pkg in $rpm_list_all ; do
#        $VMDETECTOR $pkg > /dev/null 2>&1
#        if [ $? -ne 0 ]; then
#            (cd $UNPACK_DIR/packages/ ; mv $pkg $pkg.remove)
#        fi
#    done
    # any good method?
    rpm --root $SYS_ROOT_DIR -Uvh --force $UNPACK_DIR/packages/*.rpm > ${UPDATE_TOOL_FOLDER}/log/upgrade/rpm_install.log 2>&1
    if [ $? -ne 0 ]; then
        F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "Install rpm failed." 
        F_error_log "$FUNCNAME" "`expr $LINENO - 2`" "Upgrade for OS part failed."
        F_prompt_log "Unable to install upgrade RPM packages. ${ERRMSG_ASK_HELP}."
        ERROR_NUMBER=$ERR_UPGRADE_RPMS
        return 1
    fi
    F_product_upgrade || {
        F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "Install product rpm failed." 
        F_prompt_log "Unable to install upgrade RPM packages in F_product_upgrade. ${ERRMSG_ASK_HELP}."
        ERROR_NUMBER=$ERR_UPGRADE_RPMS
        return 1
    }
    return 0
}

F_os_post_upgrade()
{
    # alter fstab file
    F_prompt_log "Update /etc/fstab file."

    old_root_partition=${HIDDEN_PARTS_ARR["old_root_partition"]}
    new_root_partition=${HIDDEN_PARTS_ARR["new_root_partition"]}
    sed -i -e "s|^$old_root_partition\b|$new_root_partition|" ${SYS_ROOT_DIR}/etc/fstab
    sed -i -e "s|^#$new_root_partition\b|#$old_root_partition|" ${SYS_ROOT_DIR}/etc/fstab

    if [ $NUM_HIDDEN_PARTS -gt 1 ]; then
        old_app_partition=${HIDDEN_PARTS_ARR["old_app_partition"]}
        new_app_partition=${HIDDEN_PARTS_ARR["new_app_partition"]}
        sed -i -e "s|^$old_app_partition\b|$new_app_partition|" ${SYS_ROOT_DIR}/etc/fstab
        sed -i -e "s|^#$new_app_partition\b|#$old_app_partition|" ${SYS_ROOT_DIR}/etc/fstab
    elif [ "${REMOVE_PARTS_ARR["old_remove_partition"]}" != "" ]; then
        old_remove_partition=${REMOVE_PARTS_ARR["old_remove_partition"]}
        sed -i -e "s|^$old_remove_partition\b|#$old_remove_partition|" ${SYS_ROOT_DIR}/etc/fstab
        if [ $NUM_REMOVE_PARTS -gt 1 ]; then
            new_remove_partition=${REMOVE_PARTS_ARR["new_remove_partition"]}
            sed -i -e "s|^$new_remove_partition\b|#$new_remove_partition|" ${SYS_ROOT_DIR}/etc/fstab
        fi
    fi

    ln -sf /boot/grub2/grub.cfg ${SYS_ROOT_DIR}/etc/

    # alter default.target
    F_prompt_log "Update /etc/systemd/system/default.target file."
    rm -f ${SYS_ROOT_DIR}/etc/systemd/system/default.target
    if [ -f /etc/systemd/system/default.target ]; then
        cp -a /etc/systemd/system/default.target ${SYS_ROOT_DIR}/etc/systemd/system/ 
    else
        ln -fs /usr/lib/systemd/system/multi-user.target ${SYS_ROOT_DIR}/etc/systemd/system/default.target
    fi

    return 0
}

F_post_upgrade() 
{
    F_os_post_upgrade || {
        F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "Post-Upgrade of OpenVA failed."
        return 1
    } && {
        F_prompt_log "Post-Upgrade of OpenVA succeeded."
    }

    F_product_post_apply || {
        F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "Post-Upgrade of product failed."
        F_prompt_log "Product defined post-apply function \"F_product_post_apply\" execute failed. ${ERRMSG_ASK_HELP}."
        ERROR_NUMBER=${ERR_PRODUCT_POST_APPLY}
        return 1
    } && {
        F_prompt_log "Post-Upgrade of product succeeded."
    }

    return 0
}

F_gen_initrdimg()
{
    F_debug_log "Generate new initrd file."

    local migration_grub2_log="/var/log/install_scripts/migration_grub2.log"
    if [ -f /usr/sbin/grub2-probe ]; then
        local boot_device=`/usr/sbin/grub2-probe --target=device /boot`
    else
	local boot_device=`df |grep /boot |grep "^/dev"| head -1 | awk '{print $1}'`
    fi

    #1. move SYS_ROOT_DIR/boot to /boot
    find /boot | grep -v '^/boot$' | grep -v '^/boot/grub2$' | grep -v '^/boot/grub2/fonts' | grep -v '^/boot/grub2/i386-pc'| grep -v '^/boot/grub2/locale' | xargs rm -rf
    #find /boot | grep -v '^/boot$' | grep -v '^/boot/grub2$' | grep -v '^/boot/grub2/fonts' | grep -v '^/boot/grub2/i386-pc'| grep -v '^/boot/grub2/locale' | grep -v '^/boot/initramfs'| grep -v '^/boot/vmlinuz' | xargs rm -rf

    # copy new os boot file
    cp --preserve=all -Rf ${NEW_OS_PLACE}/* /boot/
    cp --preserve=all -Rf ${NEW_OS_PLACE}/.v* /boot/

    #2. Mount related folder for chroot
    [ -d ${SYS_ROOT_DIR}/run/lvm ] || {
	mkdir -p ${SYS_ROOT_DIR}/run/lvm
	chmod 700 ${SYS_ROOT_DIR}/run/lvm
    }
    mount -n --bind /dev ${SYS_ROOT_DIR}/dev
    mount -n -t devpts devpts ${SYS_ROOT_DIR}/dev/pts
    mount -n -t tmpfs shm ${SYS_ROOT_DIR}/dev/shm/
    mount -n -t sysfs sysfs ${SYS_ROOT_DIR}/sys
    mount -n -t proc proc ${SYS_ROOT_DIR}/proc
    mount -n --bind /run/lvm ${SYS_ROOT_DIR}/run/lvm
    mount -n ${boot_device} ${SYS_ROOT_DIR}/boot

    #3. mkinitrd
    os_ver=`ls $NEW_OS_PLACE/initramfs-*.img | grep -v rescue | grep -v kdump | cut -d'-' -f2,3 | awk -F. '{OFS=".";NF-=1;print $0}'`
    rm -f $NEW_OS_PLACE/initramfs-*.img
chroot ${SYS_ROOT_DIR} > /dev/null 2>&1 <<EOF
    [ -d /var/log/install_scripts ] || mkdir -p /var/log/install_scripts
    echo "mkinitrd /boot/initramfs-${os_ver}.img ${os_ver}" >> ${migration_grub2_log}
    mkinitrd /boot/initramfs-${os_ver}.img ${os_ver} >> ${migration_grub2_log} 2>&1
EOF

    #4. grub2-install
    # if grub -> grub2, need call grub2-install first
    # if grub2 -> grub2, also need call grub2-install for switch to new root partition
#    if [ -f /boot/grub/grub.conf ]; then
        # /dev/sda
        grub_dev=`cat /etc/mtab |grep /boot|head -1| awk -F' ' '{print $1}' | tr -d "[0-9]"`
chroot ${SYS_ROOT_DIR} > /dev/null 2>&1 <<EOF
    echo "grub2-install --force ${grub_dev}" >> ${migration_grub2_log}
    grub2-install --force ${grub_dev} >> ${migration_grub2_log} 2>&1
EOF
#    fi

    #5. grub2-mkconfig
chroot ${SYS_ROOT_DIR} > /dev/null 2>&1 <<EOF
    echo "/usr/local/openva-update-tools/grub2-mkconfig-chroot -b ${boot_device} -o /boot/grub2/grub.cfg" >> ${migration_grub2_log}
    /usr/local/openva-update-tools/grub2-mkconfig-chroot -b ${boot_device} -o /boot/grub2/grub.cfg >> ${migration_grub2_log} 2>&1
    sed -i 's/linux16 \/boot\//linux16 \//g' /boot/grub2/grub.cfg >> ${migration_grub2_log} 2>&1
    sed -i 's/initrd16 \/boot\//initrd16 \//g' /boot/grub2/grub.cfg >> ${migration_grub2_log} 2>&1
EOF
    
    umount -f ${SYS_ROOT_DIR}/proc
    umount -f ${SYS_ROOT_DIR}/sys
    umount -f ${SYS_ROOT_DIR}/dev/shm/
    umount -f ${SYS_ROOT_DIR}/dev/pts
    umount -f ${SYS_ROOT_DIR}/dev
    umount -f ${SYS_ROOT_DIR}/run/lvm
    umount -f ${SYS_ROOT_DIR}/boot

    return 0
}

F_modify_boot_menu() 
{
    F_prompt_log "Modify grub boot menu to boot from new system."
    NEW_OS_PLACE=${SYS_ROOT_DIR}/boot/

    # backup old os
    #rm -f $BOOT_BACKUP && find /boot -type f | grep -E "`uname -r`|grub.conf" | tar -czvf $BOOT_BACKUP -T - > /dev/null 2>&1
    rm -f ${NEW_OS_PLACE}/*rescue*
    rm -f $BOOT_BACKUP && tar -czvf $BOOT_BACKUP /boot --exclude *backup.tgz --exclude "lost+found"  > /dev/null 2>&1

    local vmlinuzfile=`find ${NEW_OS_PLACE} -type f -name "vmlinuz*" | grep -v rescue | grep -v kdump | xargs basename`
    local kernelversion=${vmlinuzfile##vmlinuz-}
    local initrd=`find ${NEW_OS_PLACE} -type f -name "initramfs*" | grep -v rescue | grep -v kdump | xargs basename`

    # boot options
#    local options=`cat /proc/cmdline |cut -d' ' -f4-`

    # /dev/mapper/*
    local new_root_first=${HIDDEN_PARTS_ARR["new_root_partition"]}
    #local new_root_second="$VGNAME/${new_root_first##*$VGNAME-}"
    local old_root_first=${HIDDEN_PARTS_ARR["old_root_partition"]}

    # grub config file and boot disk
#    local grubConfig=$(readlink -f /boot/grub/grub.conf 2>/dev/null)
#    local bootroot=`cat /boot/grub/grub.conf  |grep 'splashimage' |cut -d'=' -f2 | cut -d'/' -f1`

    # add boot menu for new added kernel
#    sed -i -e "/title OpenVA/,$ d" $grubConfig

#    sed -i -e "/hiddenmenu/ a \\\
#title OpenVA ($kernelversion)\n\
#	root ${bootroot}\n\
#	kernel /${vmlinuzfile} ro root=${new_root_first} rd_LVM_LV=${new_root_second} ${options}\n\
#	initrd /${initrd} \n\
#    " $grubConfig

    # Alter /etc/default/grub
    local productName=`F_ini_get $PACKAGE_CONFIG General ProductName`
    local biosdevName=`F_ini_get $PACKAGE_CONFIG General BiosDevName`
    if [ -z "$biosdevName" ]; then
        biosdevName="0"
    elif [ "${biosdevName}" = "1" ]; then
        biosdevName="1"
    else
        biosdevName="0"
    fi

    if [ ! -f /etc/default/grub ]; then
	cat > ${SYS_ROOT_DIR}/etc/default/grub <<EOF
GRUB_TIMEOUT=5
GRUB_DEFAULT=saved
GRUB_DISABLE_SUBMENU=true
GRUB_TERMINAL_OUTPUT="console"
GRUB_CMDLINE_LINUX="rd.lvm.lv=$VGNAME/swap net.ifnames=0 rd.shell=0 rd.lvm.lv=$VGNAME/${new_root_first##*$VGNAME-} crashkernel=auto biosdevname=${biosdevName} quiet consoleblank=0"
GRUB_DISABLE_RECOVERY="true"
GRUB_DISTRIBUTOR="${productName}"
GRUB_DISABLE_OS_PROBER=true
EOF
    else
	sed -e "s/$VGNAME\/${old_root_first##*$VGNAME-}/$VGNAME\/${new_root_first##*$VGNAME-}/" -e "s/biosdevname=.* /biosdevname=${biosdevName} /g" /etc/default/grub > ${SYS_ROOT_DIR}/etc/default/grub
    fi

    F_gen_initrdimg

    #find /boot | grep -v '^/boot$' | grep -v '^/boot/grub2$' | grep -v '^/boot/grub2/fonts' | grep -v '^/boot/grub2/i386-pc'| grep -v '^/boot/grub2/locale' | xargs rm -rf

    echo "NEW_KERNEL_VER=${kernelversion}" >> $ROLLBACK_FILE

    # copy new os boot file
    #cp --preserve=all -Rf ${NEW_OS_PLACE}/* /boot/
    #cp --preserve=all -Rf ${NEW_OS_PLACE}/.v* /boot/

    return 0
}
F_pre_import_setting()
{
    F_prompt_log "Pre Import old system's setting."
    F_product_pre_import || {
        F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "Pre-import of Product failed."
        F_prompt_log "Product defined pre-import function \"F_pre_import_setting\" execute failed. ${ERRMSG_ASK_HELP}."
        ERROR_NUMBER=${ERR_PRODUCT_PRE_CHECK}
        return 1
    } && {
        F_prompt_log "Pre-Check of product succeeded."
    }

    return 0
}

F_import_setting() 
{
    F_prompt_log "Import old system's setting."

    local import_files=`F_ini_get_section $PACKAGE_CONFIG CopyFiles | grep -v '#'`
    [ -n "$import_files" ] || {
        F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "The CopyFiles section is empty."
        ERROR_NUMBER=${ERR_INI_FILE_CONTENT}
        return 1
    }
    eval $import_files
    # copy old os's necessary config files to new os
    for file in $OS_Config $App_Config ; do
        [ -e $file ] && rm -rf ${SYS_ROOT_DIR}/${file} && cp --preserve=all -Rf $file ${SYS_ROOT_DIR}/${file}
    done

    return 0
}

F_remove_snapshots()
{
    F_prompt_log "Remove snapshot logica volume."
    for (( i=0; i<$NUM_SNAPSHOT_RUNNING; i++ )) ;
    do
        local snapshot_part=${SNAPSHOT_PARTS_ARR["snapshot_data_partition$i"]}
        local sizein_le=`F_get_lvsize $snapshot_part`
        F_lv_remove $snapshot_part > /dev/null 2>&1 || {
            F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "Can't remove snapshot logical volume $snapshot_part."
        }
        lvcreate --extents ${sizein_le} --name ${snapshot_part##*$VGNAME-} $VGNAME > /dev/null 2>&1 || { 
            F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "Can't create logical volume $snapshot_part."; 
        }
    done

    return 0
}

F_die_umount()
{
    if [ "${NewDataPartMountPoint}" != "" ]; then
        mount | grep -qwE "${NewDataPartMountPoint}" && {
            umount ${NewDataPartMountPoint}
        }
    fi
    if [ $NUM_HIDDEN_PARTS -gt 1 ]; then
        mount | grep -qwE ${HIDDEN_PARTS_ARR["new_app_partition"]} && {
            umount ${HIDDEN_PARTS_ARR["new_app_partition"]}
        }
    fi

    mount | grep -qwE ${HIDDEN_PARTS_ARR["new_root_partition"]} && {
        umount ${HIDDEN_PARTS_ARR["new_root_partition"]}
    }
}

F_die_aftersnapshot()
{
    for (( i=0; i<$NUM_SNAPSHOT_RUNNING; i++ )) ;
    do
        local org_part=${SNAPSHOT_PARTS_ARR["org_data_partition$i"]}
        local snapshot_part=${SNAPSHOT_PARTS_ARR["snapshot_data_partition$i"]}
        local sizein_le=`F_get_lvsize $snapshot_part`
        F_merge_snapshot $org_part $snapshot_part > /dev/null 2>&1 || {
            F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "Can't merge snapshot logical volume $snapshot_part."
            F_lv_remove $snapshot_part
        }
        lvcreate --extents ${sizein_le} --name ${snapshot_part##*$VGNAME-} $VGNAME > /dev/null 2>&1 || { 
            F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "Can't create logical volume $snapshot_part."; 
        }
    done
    return 0
}

F_cleanup()
{
    F_prompt_log "Clear up dirty environment."
#    F_remove_snapshots
    F_die_umount
}

# entry point
F_main()
{
    # step 1: precheck
    F_pre_check || {
        F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "Pre-Check step failed."
        return 1
    } && {
        F_prompt_log "Pre-Check step finished with success."
    }

    # step 2:
    F_pre_upgrade || {
        F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "Pre-Upgrade step failed."
        return 1
    } && {
        F_prompt_log "Pre-Upgrade step finished with success."
    }


    # step 3: 
    F_prompt_log "Skip make snapshot step."
#    F_make_snapshot || {
#        F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "Make snapshot step failed."
#        return 1
#    } && {
#        F_prompt_log "Make snapshot step finished with success."
#    }

    # step 4:
    rm -f ${ROLLBACK_FILE} && touch ${ROLLBACK_FILE}

    F_upgrade || {
        F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "Upgrade step failed."
        return 1
    } && {
        F_prompt_log "Upgrade step finished with success."
    }

    F_pre_import_setting || {
        F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "Pre import setting step failed."
        return 1
    } && {
        F_prompt_log "Upgrade step finished with success."
    }

    # step 5:
    F_import_setting || {
        F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "Import setting step failed."
        return 1
    } && {
        F_prompt_log "Import system's setting step finished with success."
    }

    # step 6:
    F_post_upgrade || {
        F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "Post-upgrade step failed."
        return 1
    } && {
        F_prompt_log "Post-Upgrade step finished with success."
    }

    # step 7:
    F_modify_boot_menu || {
        F_error_log "$FUNCNAME" "`expr $LINENO - 1`" "Modify boot menu step failed."
        return 1
    } && {
        F_prompt_log "Modify boot menu step finished with success."
    }

    F_cleanup
    
    F_prompt_log "Upgrade completed with success."
    return 0
}

F_main
ret=$?

rm -f $FLAG_FILE	# remove the flag file when finish
rm -f ${ROLLBACK_FILE}

if [ $ret -ne 0 ]; then
    F_recovery
    exit ${ERROR_NUMBER}
fi

exit $ret
